package com.pan.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * 这个类主要演示线程间通信
 *
 * 要短信线程间通信的模型有两种：共享内存和消息传递
 *
 * 这里使用了原始的sychronized
 *
 *线程的状态
 * public static enum State {
 *         NEW,
 *         RUNNABLE,
 *         BLOCKED,
 *         WAITING,
 *         TIMED_WAITING,
 *         TERMINATED;
 *     }
 * @author by panstark
 * @description
 * @notice
 * @date 2021/5/5
 */
@Slf4j
public class ThreadCommunicate {

    public static void main(String[] args) {
        ThreadCommunicate threadCommunicate = new ThreadCommunicate();
//        //没有synchronized包裹的wait会报错
//        threadCommunicate.waitTest1();
//        //我等到永远，所以要注掉，不然下面没法走了
//        threadCommunicate.waitTest2();
//        //我等你5s
        threadCommunicate.waitTest3();
//        //没有synchronized包裹的notify会报错
        threadCommunicate.notifyTest();
//        //白雪公主与白马王子之间有一个电话
        threadCommunicate.havePhone(threadCommunicate);
//        //通过共享数据完成通信，白雪公主和白马王子打通了电话
//        threadCommunicate.shareVolatie();
//
//        //经典面试题，三个线程分别打印A\B\C三个字母10遍
//        threadCommunicate.printABC1();
//        threadCommunicate.printABC2();
//        threadCommunicate.printABC3();
//        threadCommunicate.printABC4();
//        threadCommunicate.interruptTest();
    }

    /**
     * 基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想，
     * 大致意思就是多个线程同时监听一个变量，当这个变量发生变化的时候 ，
     * 线程能够感知并执行相应的业务。这也是最简单的一种实现方式
     */
    public volatile boolean connect =false;

    /**
     * 用来当做对象锁
     */
    public Object phone1 = new Object();

    /**
     * 通过一个共享变量
     * 实现线程间的通信
     */
    public void shareVolatie(){
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 100; i++) {
                    if(i==50){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        log.info("50年后，白马王子给白雪公主的电话接通了");
                        connect=true;
                        break;
                    }
                }
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {

                while(true){
                    if(connect){
                        log.info("两个老人，热泪盈眶，you jump，I jump。");
                        break;
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }


    /**
     * notify 需要在synchronized中才能正常使用
     */
    public void notifyTest(){
        log.info("王子终于来了，他没有找到白雪公主，直接大喊notify");
        try{
            notify();
        }catch (Exception e){
            log.error("没有synchronized的notify是会报错的");
        }

    }

    /**
     * wait 需要在synchronized中才能正常使用
     */
    public void waitTest1(){

        log.info("白雪公主没给发出自己的地址，等待着王子的到来，会报错的。");
        try {
            wait();
        } catch (Exception e) {
            log.error("wait 必须在synchronized的包裹下，才能正常使用");
        }
    }

    /**
     * 无限等待
     * synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁，同时属于可重 入锁
     * Synchronized
     *
     *  1) Wait Set：哪些调用wait方法被阻塞的线程被放置在这里；
     *  2) Contention List：竞争队列，所有请求锁的线程首先被放在这个竞争队列中；
     *  3) Entry List：Contention List中那些有资格成为候选资源的线程被移动到Entry List中；
     *  4) OnDeck：任意时刻，多只有一个线程正在竞争锁资源，该线程被称为OnDeck；
     *  5) Owner：当前已经获取到所资源的线程被称为Owner；
     *  6) !Owner：当前释放锁的线程。
     */
    public  synchronized void waitTest2(){

        log.info("增加了synchronized，wait方法会把当前调用线程阻塞。相当于自己打晕了自己，给别人机会,当前线程是{}！" +
                "可以看到系统一直处于运行状态无法停止，就像白雪公主永远等不到她的白马王子。",Thread.currentThread().getName());
        try {
            wait();
        } catch (Exception e) {
            log.info("wait 必须在synchronized的包裹下，才能正常使用");
            e.printStackTrace();
        }
    }

    /**
     * 等你5s
     */
    public synchronized void waitTest3(){
        log.info("为了不永远的等待，这份约定增加了一个期限，如果5s内，你还没有和我道歉，那咱们就分手吧。当前线程是{}",Thread.currentThread().getName());
        try {
            wait(5000);
        } catch (Exception e) {
            log.info("wait 必须在synchronized的包裹下，才能正常使用");
            e.printStackTrace();
        }
        log.info("5s过去了，白雪公主和白马王子分手了，勿念，祝越来越好。");
    }

    /**
     * 在电话1中睡了
     */
    public void waitWithPhone(){

        synchronized (phone1){
            log.info("在phone1包裹的synchronized中，白雪公主睡了。当前公主是{}。",Thread.currentThread().getName());
            try {
                phone1.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("白雪公主接到了白马王子的电话，醒了。");
        }
    }

    /**
     * 打电话，吵醒白雪公主
     */
    public void notifyWithPhone(){

        synchronized (phone1){
            log.info("在phone1包裹的synchronized中,白马王子来了。当前白马是{}。",Thread.currentThread().getName());
            phone1.notify();
        }
    }

    /**
     * 有电话，两个线程才能通信
     * @param threadCommunicate
     */
    public void havePhone(ThreadCommunicate threadCommunicate){
        Thread snowPrincess = new Thread(new Runnable() {
            @Override
            public void run() {
                threadCommunicate.waitWithPhone();
            }
        });
        Thread horsePrice = new Thread(new Runnable() {
            @Override
            public void run() {
                threadCommunicate.notifyWithPhone();
            }
        });
        snowPrincess.start();
        try {
            Thread.sleep(100);
            horsePrice.start();
            Thread.sleep(1000);
            log.info("白马王子顺利的通知了白雪公主，两个人过上了幸福的生活");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printABC1(){

        Object phoneA = new Object();
        Object phoneB = new Object();
        Object phoneC = new Object();

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    synchronized (phoneC){
                        synchronized (phoneA){
                            log.info("---A----");
                            //A打印完了，要去通知B线程，然后在运行完synchronized块后，释放A锁
                            phoneA.notify();

                        }
                        if(i<=9){
                            try {
                                //A线程需要等待C线程的通知，所以用phoneC.wait()，同时释放phoneC锁
                                phoneC.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    synchronized (phoneA){
                        synchronized (phoneB){
                            log.info("---B----");
                            //通知B去打印
                            phoneB.notify();

                        }
                        if(i<=9){
                            try {
                                phoneA.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    synchronized (phoneB){
                        synchronized (phoneC){
                            log.info("---C----");
                            //通知B去打印
                            phoneC.notify();
                        }
                        if(i<=9){
                            try {
                                phoneB.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });

        try {
            threadA.start();
            Thread.sleep(1000);
            threadB.start();
            Thread.sleep(1000);
            threadC.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printABC2(){
        Object phoneA = new Object();
        Object phoneB = new Object();
        Object phoneC = new Object();
        Thread threadA = new Thread(new PrintABC(phoneC,phoneA,'a'));
        Thread threadB = new Thread(new PrintABC(phoneA,phoneB,'b'));
        Thread threadC = new Thread(new PrintABC(phoneB,phoneC,'c'));

        try {
            threadA.start();
            Thread.sleep(1000);
            threadB.start();
            Thread.sleep(1000);
            threadC.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //线程可感知公共变量
     volatile int index = 0;
    ReentrantLock lock =new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();

    private void printABC3() {

        ThreadCommunicate threadCommunicate = new ThreadCommunicate();

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    threadCommunicate.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    threadCommunicate.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    threadCommunicate.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();
        threadB.start();
        threadC.start();
    }



    /**
     * lock锁与synchronized锁效果一样
     */
    public void printA() throws InterruptedException {

        lock.lock();
        try {
            int num=0;
            while(num<=9){
                if(index!=0){
                    conditionA.await();
                }
                log.info("----A----");
                num++;
                index=1;
                conditionB.signal();
            }
        }finally {
            lock.unlock();
        }

    }

    public  void printB() throws InterruptedException {
        lock.lock();
        try {
            int num=0;
            while(num<=9){
                if(index!=1){
                    conditionB.await();
                }
                log.info("----B----");
                num++;
                index=2;
                conditionC.signal();
            }
        }finally {
            lock.unlock();
        }
    }

    public  void printC() throws InterruptedException {
        lock.lock();
        try {
            int num=0;
            while(num<=9){
                if(index!=2){
                    conditionC.await();
                }
                log.info("----C----");
                num++;
                index=0;
                conditionA.signal();
            }
        }finally {
            lock.unlock();
        }
    }


    static volatile int state =0;
    /**
     * 仅使用一个变量
     * 实现多线程打印
     */
    private void printABC4() {

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                int num =0;
                while(num<=9){
                    if(state%3==0){
                        log.info("----A---");
                        state++;
                        num++;
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                int num =0;
                while(num<=9){
                    if(state%3==1){
                        log.info("----B---");
                        state++;
                        num++;
                    }
                }
            }
        });

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                int num =0;
                while(num<=9){
                    if(state%3==2){
                        log.info("----C---");
                        state++;
                        num++;
                    }
                }
            }
        });


        try {
            threadA.start();
            Thread.sleep(100);
            threadB.start();
            Thread.sleep(100);
            threadC.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * 作为一种协作机制，不会强求被中断线程一定要在某个点进行处理。
     * 实际上，被中断线程只需在合适的时候处理即可，如果没有合适的时间点，甚至可以不处理，
     * 这时候在任务处理层面，就跟没有调用中断方法一样。“合适的时候”与线程正在处理的业务逻辑紧密相关，
     * 例如，每次迭代的时候，进入一个可能阻塞且无法中断的方法之前等，但多半不会出现在某个临界区更新另一个对象状态的时候，
     * 因为这可能会导致对象处于不一致状态。
     *
     * 中断的使用
     * 通常，中断的使用场景有以下几个：
     *
     * 点击某个桌面应用中的取消按钮时；
     * 某个操作超过了一定的执行时间限制需要中止时；
     * 多个线程做相同的事情，只要一个线程成功其它线程都可以取消时；
     * 一组线程中的一个或多个出现错误导致整组都无法继续时；
     * 当一个应用或服务需要停止时。
     */
    public void interruptTest(){
     Thread whiteHorsePrince= new Thread(new Runnable() {
         int i=0;
         @Override
         public void run() {
             while(true){
                 i++;
                 if(i%1000==0){
                     log.info("我想做一个雪糕，请给我一些时间{}",i);
                     if(Thread.currentThread().isInterrupted()){
                         //测试了几次，虽然是中断状态，但是从来都没有中断过···一直还在执行。
                         log.info("我现在的状态是中断，但是系统希望我再运行一段时间吧，白雪公主需要耐心的等待。{}",Thread.currentThread().getName());
                         Thread.currentThread().interrupt();
                     }
                 }
             }
         }
     });

     //模拟用户中断某个线程
     Thread snowPrincess = new Thread(new Runnable() {
         int time =0;
         @Override
         public void run() {
             while(true){
                 try {
                     Thread.sleep(10);
                     time++;
                     //用户等的太久了，想要停止这个操作
                     if(time>50){
                         log.info("白雪公主不想等了，中止了这个操作。{}",Thread.currentThread().getName());
                         whiteHorsePrince.interrupt();
                         break;
                     }
                 } catch (InterruptedException e) {
                     log.info("白雪公主中断了别人，自己并不回报错");
                     e.printStackTrace();
                 }
             }
         }
     });

        whiteHorsePrince.start();
        snowPrincess.start();
    }




    class PrintABC implements Runnable{

        Object next;
        Object current;
        char print;

        public PrintABC(Object next,Object current,char print){
            this.next=next;
            this.current=current;
            this.print = print;
        }


        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                synchronized (next){
                    synchronized (current){
                        log.info("---{}---",print);
                        current.notify();
                    }
                    if(i<9){
                        try {
                            next.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            }
        }
    }


}




